initial commit
This commit is contained in:
commit
3958100391
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/.settings
|
||||
/bin
|
||||
/.classpath
|
||||
/TEST
|
||||
/target/
|
23
.project
Normal file
23
.project
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>DesktopStreamDeck</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
63
pom.xml
Normal file
63
pom.xml
Normal file
@ -0,0 +1,63 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>me.mrletsplay</groupId>
|
||||
<artifactId>StreamDeckDesktop</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<build>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<release>17</release>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>me.mrletsplay.streamdeck.StreamDeck</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.4.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>Graphite-Official</id>
|
||||
<url>https://maven.graphite-official.com/releases</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>me.mrletsplay</groupId>
|
||||
<artifactId>SimpleHTTPServer</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.mrletsplay</groupId>
|
||||
<artifactId>MrCore</artifactId>
|
||||
<version>4.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
86
src/main/java/me/mrletsplay/streamdeck/Soundboard.java
Normal file
86
src/main/java/me/mrletsplay/streamdeck/Soundboard.java
Normal file
@ -0,0 +1,86 @@
|
||||
package me.mrletsplay.streamdeck;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.Clip;
|
||||
import javax.sound.sampled.FloatControl;
|
||||
import javax.sound.sampled.LineEvent;
|
||||
import javax.sound.sampled.LineUnavailableException;
|
||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||
|
||||
import me.mrletsplay.streamdeck.exception.InitializationException;
|
||||
import me.mrletsplay.streamdeck.exception.SoundboardException;
|
||||
|
||||
public class Soundboard {
|
||||
|
||||
private static ExecutorService executor;
|
||||
|
||||
public static void initialize() throws InitializationException {
|
||||
executor = Executors.newCachedThreadPool();
|
||||
}
|
||||
|
||||
public static void finish() {
|
||||
executor.shutdown();
|
||||
}
|
||||
|
||||
public static void play(String path) throws SoundboardException {
|
||||
AudioInputStream s;
|
||||
try {
|
||||
s = AudioSystem.getAudioInputStream(new File("TEST/test.wav"));
|
||||
} catch (UnsupportedAudioFileException | IOException e) {
|
||||
throw new SoundboardException(e);
|
||||
}
|
||||
|
||||
executor.submit(() -> {
|
||||
try(Clip clip = AudioSystem.getClip()) {
|
||||
Object wait = new Object();
|
||||
AtomicBoolean playing = new AtomicBoolean(true);
|
||||
clip.addLineListener(event -> {
|
||||
if(event.getType() == LineEvent.Type.STOP || event.getType() == LineEvent.Type.CLOSE) {
|
||||
playing.set(false);
|
||||
wait.notify();
|
||||
}
|
||||
});
|
||||
|
||||
clip.open(s);
|
||||
clip.setFramePosition(0);
|
||||
|
||||
// FloatControl gain = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
|
||||
// gain.setValue(20f * (float) Math.log10(0.2));
|
||||
|
||||
clip.start();
|
||||
|
||||
while(playing.get()) { wait.wait(); };
|
||||
} catch (LineUnavailableException | IOException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void test() throws IOException, UnsupportedAudioFileException, LineUnavailableException {
|
||||
AudioInputStream s = AudioSystem.getAudioInputStream(new File("TEST/test.wav"));
|
||||
try(Clip c = AudioSystem.getClip()) {
|
||||
AtomicBoolean e = new AtomicBoolean(true);
|
||||
c.addLineListener(event -> {
|
||||
if(event.getType() == LineEvent.Type.STOP) e.set(false);
|
||||
});
|
||||
c.open(s);
|
||||
|
||||
c.setFramePosition(0);
|
||||
FloatControl gain = (FloatControl) c.getControl(FloatControl.Type.MASTER_GAIN);
|
||||
gain.setValue(20f * (float) Math.log10(0.2));
|
||||
c.start();
|
||||
|
||||
c.loop(10);
|
||||
|
||||
while(e.get());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
37
src/main/java/me/mrletsplay/streamdeck/StreamDeck.java
Normal file
37
src/main/java/me/mrletsplay/streamdeck/StreamDeck.java
Normal file
@ -0,0 +1,37 @@
|
||||
package me.mrletsplay.streamdeck;
|
||||
|
||||
import me.mrletsplay.simplehttpserver.http.server.HttpServer;
|
||||
import me.mrletsplay.streamdeck.action.ActionSerializer;
|
||||
import me.mrletsplay.streamdeck.api.StreamDeckAPI;
|
||||
import me.mrletsplay.streamdeck.deck.Deck;
|
||||
import me.mrletsplay.streamdeck.exception.InitializationException;
|
||||
|
||||
public class StreamDeck {
|
||||
|
||||
private static Deck deck;
|
||||
|
||||
public static void main(String[] args) throws InitializationException {
|
||||
deck = new Deck(20);
|
||||
|
||||
ActionSerializer.initialize();
|
||||
Soundboard.initialize();
|
||||
|
||||
HttpServer server = new HttpServer(HttpServer.newConfigurationBuilder()
|
||||
.hostBindAll()
|
||||
.port(5734)
|
||||
.create());
|
||||
|
||||
new StreamDeckAPI().register(server.getDocumentProvider());
|
||||
|
||||
server.start();
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
Soundboard.finish();
|
||||
}));
|
||||
}
|
||||
|
||||
public static Deck getDeck() {
|
||||
return deck;
|
||||
}
|
||||
|
||||
}
|
14
src/main/java/me/mrletsplay/streamdeck/action/Action.java
Normal file
14
src/main/java/me/mrletsplay/streamdeck/action/Action.java
Normal file
@ -0,0 +1,14 @@
|
||||
package me.mrletsplay.streamdeck.action;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Action {
|
||||
|
||||
String value();
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package me.mrletsplay.streamdeck.action;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ActionParameter {
|
||||
|
||||
String name() default "";
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package me.mrletsplay.streamdeck.action;
|
||||
|
||||
public class ActionSerializationException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 5454427202803805714L;
|
||||
|
||||
public ActionSerializationException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ActionSerializationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ActionSerializationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ActionSerializationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
package me.mrletsplay.streamdeck.action;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import me.mrletsplay.mrcore.json.JSONObject;
|
||||
import me.mrletsplay.mrcore.json.JSONType;
|
||||
import me.mrletsplay.simplehttpserver.http.validation.JsonObjectValidator;
|
||||
import me.mrletsplay.streamdeck.action.impl.PlaySoundAction;
|
||||
import me.mrletsplay.streamdeck.action.impl.PressKeyAction;
|
||||
import me.mrletsplay.streamdeck.exception.InitializationException;
|
||||
|
||||
public class ActionSerializer {
|
||||
|
||||
public static final JsonObjectValidator
|
||||
ACTION_VALIDATOR = new JsonObjectValidator()
|
||||
.require("type", JSONType.STRING)
|
||||
.require("data", JSONType.OBJECT);
|
||||
|
||||
private static final List<Class<? extends DeckAction>> ACTIONS = List.of(
|
||||
PlaySoundAction.class,
|
||||
PressKeyAction.class
|
||||
);
|
||||
|
||||
private static Map<String, Class<? extends DeckAction>> nameToAction;
|
||||
|
||||
public static void initialize() throws InitializationException {
|
||||
Map<String, Class<? extends DeckAction>> nta = new LinkedHashMap<>();
|
||||
for(var actionClass : ACTIONS) {
|
||||
Action ac = actionClass.getAnnotation(Action.class);
|
||||
if(ac == null) throw new InitializationException("Class " + actionClass.getName() + " is missing the @Action annotation");
|
||||
|
||||
nta.put(ac.value(), actionClass);
|
||||
}
|
||||
|
||||
nameToAction = Collections.unmodifiableMap(nta);
|
||||
}
|
||||
|
||||
public static JSONObject serializeAction(DeckAction action) throws ActionSerializationException {
|
||||
if(nameToAction == null) throw new ActionSerializationException("ActionSerializer is not initialized");
|
||||
|
||||
Class<? extends DeckAction> actionClass = action.getClass();
|
||||
if(!ACTIONS.contains(actionClass)) throw new ActionSerializationException("Class " + actionClass.getName() + " is not a registered action class");
|
||||
|
||||
Action ac = actionClass.getAnnotation(Action.class);
|
||||
|
||||
JSONObject object = new JSONObject();
|
||||
object.put("type", ac.value());
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
for(Field f : action.getClass().getDeclaredFields()) {
|
||||
ActionParameter parameter = f.getAnnotation(ActionParameter.class);
|
||||
if(parameter == null) continue;
|
||||
|
||||
String name = !parameter.name().isEmpty() ? parameter.name() : f.getName();
|
||||
try {
|
||||
f.setAccessible(true);
|
||||
data.put(name, f.get(action));
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
throw new ActionSerializationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
action.serialize(data);
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
public static DeckAction deserializeAction(JSONObject action) throws ActionSerializationException {
|
||||
if(nameToAction == null) throw new ActionSerializationException("ActionSerializer is not initialized");
|
||||
|
||||
String actionType = action.optString("type").orElseThrow(() -> new ActionSerializationException("Missing action type"));
|
||||
Class<? extends DeckAction> actionClass = nameToAction.get(actionType);
|
||||
if(actionClass == null) throw new ActionSerializationException("Action doesn't exist");
|
||||
|
||||
JSONObject data = action.optJSONObject("data").orElseThrow(() -> new ActionSerializationException("Missing data"));
|
||||
|
||||
try {
|
||||
Constructor<? extends DeckAction> constr = actionClass.getDeclaredConstructor();
|
||||
constr.setAccessible(true);
|
||||
DeckAction a = constr.newInstance();
|
||||
|
||||
for(Field f : action.getClass().getDeclaredFields()) {
|
||||
ActionParameter parameter = f.getAnnotation(ActionParameter.class);
|
||||
if(parameter == null) continue;
|
||||
|
||||
String name = !parameter.name().isEmpty() ? parameter.name() : f.getName();
|
||||
try {
|
||||
f.setAccessible(true);
|
||||
|
||||
// TODO: allow JSONSerializable
|
||||
f.set(a, JSONType.castJSONValueTo(data.get(name), f.getType()));
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
throw new ActionSerializationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
a.deserialize(data);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
|
||||
throw new ActionSerializationException(e);
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package me.mrletsplay.streamdeck.action;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import me.mrletsplay.mrcore.json.JSONObject;
|
||||
|
||||
public interface DeckAction {
|
||||
|
||||
/**
|
||||
* Useful if parameters can't be directly mapped to JSON types.<br>
|
||||
* By default, this does nothing but may be implemented if needed
|
||||
* @param data The action data
|
||||
* @throws ActionSerializationException If the action can't be deserialized
|
||||
*/
|
||||
public default void deserialize(JSONObject data) throws ActionSerializationException {}
|
||||
|
||||
/**
|
||||
* Must be implemented to correspond to {@link #deserialize(JSONObject)}.<br>
|
||||
* By default, this does nothing but may be implemented if needed
|
||||
* @param data The action data
|
||||
* @throws ActionSerializationException If the action can't be serialized
|
||||
*/
|
||||
public default void serialize(JSONObject data) throws ActionSerializationException {}
|
||||
|
||||
/**
|
||||
* Validates the action and returns a list of errors, or an empty list if there are no errors.<br>
|
||||
* By default, this does nothing but may be implemented to add further validation
|
||||
* @return A list of errors, or an empty list if there are no errors
|
||||
*/
|
||||
public default List<String> validate() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the action
|
||||
*/
|
||||
public void run();
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package me.mrletsplay.streamdeck.action.impl;
|
||||
|
||||
import me.mrletsplay.streamdeck.action.Action;
|
||||
import me.mrletsplay.streamdeck.action.ActionParameter;
|
||||
import me.mrletsplay.streamdeck.action.DeckAction;
|
||||
|
||||
@Action("play_sound")
|
||||
public final class PlaySoundAction implements DeckAction {
|
||||
|
||||
@ActionParameter
|
||||
private String soundPath;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package me.mrletsplay.streamdeck.action.impl;
|
||||
|
||||
import java.awt.AWTException;
|
||||
import java.awt.Robot;
|
||||
|
||||
import me.mrletsplay.streamdeck.action.Action;
|
||||
import me.mrletsplay.streamdeck.action.ActionParameter;
|
||||
import me.mrletsplay.streamdeck.action.DeckAction;
|
||||
|
||||
@Action("press_key")
|
||||
public final class PressKeyAction implements DeckAction {
|
||||
|
||||
private static Robot robot;
|
||||
|
||||
static {
|
||||
try {
|
||||
robot = new Robot();
|
||||
} catch (AWTException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ActionParameter
|
||||
private int key;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
robot.keyPress(key);
|
||||
robot.delay(100);
|
||||
robot.keyRelease(key);
|
||||
}
|
||||
|
||||
}
|
149
src/main/java/me/mrletsplay/streamdeck/api/StreamDeckAPI.java
Normal file
149
src/main/java/me/mrletsplay/streamdeck/api/StreamDeckAPI.java
Normal file
@ -0,0 +1,149 @@
|
||||
package me.mrletsplay.streamdeck.api;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import me.mrletsplay.mrcore.json.JSONArray;
|
||||
import me.mrletsplay.mrcore.json.JSONObject;
|
||||
import me.mrletsplay.mrcore.json.JSONType;
|
||||
import me.mrletsplay.simplehttpserver.http.HttpRequestMethod;
|
||||
import me.mrletsplay.simplehttpserver.http.HttpStatusCodes;
|
||||
import me.mrletsplay.simplehttpserver.http.endpoint.Endpoint;
|
||||
import me.mrletsplay.simplehttpserver.http.endpoint.EndpointCollection;
|
||||
import me.mrletsplay.simplehttpserver.http.header.DefaultClientContentTypes;
|
||||
import me.mrletsplay.simplehttpserver.http.request.HttpRequestContext;
|
||||
import me.mrletsplay.simplehttpserver.http.response.JsonResponse;
|
||||
import me.mrletsplay.simplehttpserver.http.validation.JsonArrayValidator;
|
||||
import me.mrletsplay.simplehttpserver.http.validation.JsonObjectValidator;
|
||||
import me.mrletsplay.simplehttpserver.http.validation.result.ValidationResult;
|
||||
import me.mrletsplay.streamdeck.StreamDeck;
|
||||
import me.mrletsplay.streamdeck.action.ActionSerializationException;
|
||||
import me.mrletsplay.streamdeck.action.ActionSerializer;
|
||||
import me.mrletsplay.streamdeck.action.DeckAction;
|
||||
import me.mrletsplay.streamdeck.deck.DeckButton;
|
||||
|
||||
public class StreamDeckAPI implements EndpointCollection {
|
||||
|
||||
private static final JsonObjectValidator
|
||||
BUTTON_VALIDATOR = new JsonObjectValidator()
|
||||
.require("id", JSONType.INTEGER)
|
||||
.optional("icon", JSONType.STRING)
|
||||
.optionalObject("action", ActionSerializer.ACTION_VALIDATOR),
|
||||
STATE_VALIDATOR = new JsonObjectValidator()
|
||||
.requireArray("buttons", new JsonArrayValidator()
|
||||
.requireElementObjects(BUTTON_VALIDATOR));
|
||||
|
||||
private static JsonResponse error(String errorMessage) {
|
||||
JSONObject error = new JSONObject();
|
||||
error.put("error", errorMessage);
|
||||
return new JsonResponse(error);
|
||||
}
|
||||
|
||||
@Endpoint(method = HttpRequestMethod.GET, path = "/actions")
|
||||
public void getActions() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Endpoint(method = HttpRequestMethod.GET, path = "/state")
|
||||
public void getState() {
|
||||
HttpRequestContext ctx = HttpRequestContext.getCurrentContext();
|
||||
JSONObject state = new JSONObject();
|
||||
|
||||
JSONArray buttons = new JSONArray();
|
||||
for(DeckButton button : StreamDeck.getDeck().getButtons()) {
|
||||
JSONObject buttonObj = new JSONObject();
|
||||
buttonObj.put("id", button.getId());
|
||||
|
||||
if(button.getIcon() == null) {
|
||||
buttonObj.put("icon", null);
|
||||
}else {
|
||||
try {
|
||||
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
|
||||
ImageIO.write(button.getIcon(), "PNG", bOut);
|
||||
}catch(IOException e) {
|
||||
ctx.respond(HttpStatusCodes.INTERNAL_SERVER_ERROR_500, error("Failed to serialize icon: " + e.getMessage()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(button.getAction() == null) {
|
||||
buttonObj.put("action", null);
|
||||
}else {
|
||||
try {
|
||||
buttonObj.put("action", ActionSerializer.serializeAction(button.getAction()));
|
||||
}catch(ActionSerializationException e) {
|
||||
ctx.respond(HttpStatusCodes.INTERNAL_SERVER_ERROR_500, error("Failed to serialize action " + e.getMessage()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
buttons.add(buttonObj);
|
||||
}
|
||||
|
||||
state.put("buttons", buttons);
|
||||
|
||||
ctx.respond(HttpStatusCodes.OK_200, new JsonResponse(buttons));
|
||||
}
|
||||
|
||||
@Endpoint(method = HttpRequestMethod.PATCH, path = "/state")
|
||||
public void updateState() {
|
||||
HttpRequestContext ctx = HttpRequestContext.getCurrentContext();
|
||||
JSONObject content;
|
||||
if((content = ctx.expectContent(DefaultClientContentTypes.JSON_OBJECT)) == null) {
|
||||
ctx.respond(HttpStatusCodes.BAD_REQUEST_400, error("Invalid content"));
|
||||
return;
|
||||
}
|
||||
|
||||
ValidationResult result = STATE_VALIDATOR.validate(content);
|
||||
if(!result.isOk()) {
|
||||
ctx.respond(HttpStatusCodes.BAD_REQUEST_400, result.asJsonResponse());
|
||||
return;
|
||||
}
|
||||
|
||||
for(Object o : content.getJSONArray("buttons")) {
|
||||
JSONObject buttonObj = (JSONObject) o;
|
||||
int id = buttonObj.getInt("id");
|
||||
DeckButton button = StreamDeck.getDeck().getButton(id);
|
||||
|
||||
if(buttonObj.has("icon")) {
|
||||
String icon = buttonObj.getString("icon");
|
||||
if(icon == null) {
|
||||
button.setIcon(null);
|
||||
}else {
|
||||
try {
|
||||
button.setIcon(ImageIO.read(new ByteArrayInputStream(Base64.getDecoder().decode(icon))));
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
ctx.respond(HttpStatusCodes.BAD_REQUEST_400, error("Invalid icon"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(buttonObj.has("action")) {
|
||||
JSONObject action = buttonObj.getJSONObject("action");
|
||||
if(action == null) {
|
||||
button.setAction(null);
|
||||
}else {
|
||||
DeckAction newAction;
|
||||
try {
|
||||
newAction = ActionSerializer.deserializeAction(action);
|
||||
} catch (ActionSerializationException e) {
|
||||
ctx.respond(HttpStatusCodes.BAD_REQUEST_400, error("Invalid action: " + e.getMessage()));
|
||||
return;
|
||||
}
|
||||
|
||||
button.setAction(newAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBasePath() {
|
||||
return "/api";
|
||||
}
|
||||
|
||||
}
|
26
src/main/java/me/mrletsplay/streamdeck/deck/Deck.java
Normal file
26
src/main/java/me/mrletsplay/streamdeck/deck/Deck.java
Normal file
@ -0,0 +1,26 @@
|
||||
package me.mrletsplay.streamdeck.deck;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class Deck {
|
||||
|
||||
private List<DeckButton> buttons;
|
||||
|
||||
public Deck(int buttonCount) {
|
||||
List<DeckButton> buttons = new ArrayList<>(buttonCount);
|
||||
for(int i = 0; i < buttonCount; i++) buttons.add(new DeckButton(i));
|
||||
this.buttons = Collections.unmodifiableList(buttons);
|
||||
}
|
||||
|
||||
public List<DeckButton> getButtons() {
|
||||
return buttons;
|
||||
}
|
||||
|
||||
public DeckButton getButton(int id) {
|
||||
if(id < 0 || id >= buttons.size()) return null;
|
||||
return buttons.get(id);
|
||||
}
|
||||
|
||||
}
|
37
src/main/java/me/mrletsplay/streamdeck/deck/DeckButton.java
Normal file
37
src/main/java/me/mrletsplay/streamdeck/deck/DeckButton.java
Normal file
@ -0,0 +1,37 @@
|
||||
package me.mrletsplay.streamdeck.deck;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import me.mrletsplay.streamdeck.action.DeckAction;
|
||||
|
||||
public class DeckButton {
|
||||
|
||||
private int id;
|
||||
private BufferedImage icon;
|
||||
private DeckAction action;
|
||||
|
||||
public DeckButton(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setIcon(BufferedImage icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public BufferedImage getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void setAction(DeckAction action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public DeckAction getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package me.mrletsplay.streamdeck.exception;
|
||||
|
||||
public class InitializationException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 8812199845001173928L;
|
||||
|
||||
public InitializationException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public InitializationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public InitializationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InitializationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package me.mrletsplay.streamdeck.exception;
|
||||
|
||||
public class SoundboardException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 8790973950212930162L;
|
||||
|
||||
public SoundboardException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SoundboardException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public SoundboardException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SoundboardException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user